package com.ruoyi.web.controller.app;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.system.domain.fx.FxOrderDada;
import com.ruoyi.system.domain.fx.FxOrderItem;
import com.ruoyi.system.domain.vo.FxOrderVo;
import com.ruoyi.system.service.fx.IFxOrderDadaService;
import com.ruoyi.system.service.fx.IFxOrderService;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/fx/dada")
public class AppDaDaController {

    private static final Logger log = LoggerFactory.getLogger(AppDaDaController.class);

    @Autowired
    public IFxOrderService fxOrderService;

    @Autowired
    public IFxOrderDadaService orderDadaService;

    @Value("${dada.apiUrl}")
    private String devUrl;

    @Value("${dada.sourceId}")
    private String sourceId;

    @Value("${dada.sourceNo}")
    private String sourceNo;

    @Value("${dada.appKey}")
    private String appKey;

    @Value("${dada.appSecret}")
    private String appSecret;

    @Value("${dada.callback}")
    private String callback;

    /* 构建请求参数，完整示例可以参考：
     * @param body 接口的body参数内容
     * @return 构建真正的api接口参数
     */
    private Map<String, Object> generateParams(Object body) {

        Map<String, Object> data = new HashMap<>();
        data.put("source_id", sourceId);
        data.put("app_key", appKey);
        data.put("timestamp", Instant.now().getEpochSecond());
        data.put("format", "json");
        data.put("v", "1.0");
        data.put("body", JSON.toJSONString(body));
        data.put("signature", signature(data, appSecret));
        return data;
    }

    private String signature(Map<String, Object> data, String appSecret) {
        // 请求参数按照【属性名】字典升序排序后，按照属性名+属性值拼接
        String signStr = data.keySet().stream()
                .sorted()
                .map(it -> String.format("%s%s", it, data.get(it)))
                .collect(Collectors.joining(""));

        // 拼接后的结果首尾加上appSecret
        String finalSignStr = appSecret + signStr + appSecret;

        // MD5加密并转为大写
        return DigestUtils.md5Hex(finalSignStr).toUpperCase();
    }

    /**
     * 直接下单回调
     * @param jsonObject
     */
    @RequestMapping("/addOrderCallback")
    public void addOrderCallback(@RequestBody JSONObject jsonObject){
        log.info(jsonObject.toJSONString());
        //下滑线转驼峰
        convert(jsonObject);
        FxOrderDada orderDada = JSONObject.parseObject(jsonObject.toJSONString(), FxOrderDada.class);
        orderDadaService.insertFxOrderDada(orderDada);
    }

    /**
     * 直接下单
     * @return
     */
    @GetMapping("/addOrder")
    public AjaxResult addOrder(String orderId){
        //订单商品数量
        Integer num = 0;
        FxOrderVo order = fxOrderService.selectFxOrderByOrderId(orderId);

        Map<String, Object> data = new HashMap<>();
        List<Map<String,String>> productList = new ArrayList<>();

        //组装商品信息
        for (FxOrderItem item : order.getFxOrderItemList()) {
            num += Integer.parseInt(item.getGoodCount());

            Map<String,String> product = new HashMap<>();
            //商品名称，限制长度128
            product.put("sku_name",item.getGoodName());
            //商品编码，限制长度64
            product.put("src_product_no", String.valueOf(item.getStoreGoodId()));
            //商品数量，精确到小数点后两位
            product.put("count",item.getGoodCount());
            //商品单位，默认：件
            product.put("unit","件");
            productList.add(product);
        }

        //订单商品数量
        data.put("cargo_num", num);
        //订单商品明细
        data.put("product_list", productList);
        //门店编号，门店创建后可在门店列表查看
        data.put("shop_no", sourceNo);
        //第三方订单ID
        data.put("origin_id", order.getId());
        //订单金额（单位：元）
        data.put("cargo_price", order.getPayMoney());
        //是否需要垫付 1:是 0:否 (垫付订单金额，非运费)
        data.put("is_prepay", 0);
        //收货人姓名
        data.put("receiver_name", order.getReceiveName());
        //收货人地址
        data.put("receiver_address", order.getReceiveAddress());
        //收货人地址纬度（支持火星坐标系(高德、腾讯地图))
        data.put("receiver_lat", order.getReceiveLatitude());
        //收货人地址经度（支持火星坐标系(高德、腾讯地图))
        data.put("receiver_lng", order.getReceiveLongitude());
        //回调URL（查看回调说明）
        data.put("callback", callback+"/fx/dada/addOrderCallback");
        //订单重量（单位：Kg）
        data.put("cargo_weight", 1);
        //收货人手机号（手机号和座机号必填一项），如传入隐私号，请使用英文逗号","分隔格式，并确保支持接收短信
        data.put("receiver_phone", order.getReceivePhonenum());
        //收货人座机号（手机号和座机号必填一项）
        data.put("receiver_tel", order.getReceivePhonenum());
        //订单备注
        data.put("info", order.getReceiveRemark());
        //支持配送的物品品类见链接。请选择门店真实配送品类，如无法判断可咨询您的销售经理。
        data.put("cargo_type", 25);

        //订单来源标识
        data.put("origin_mark", "");
        //订单来源编号
        data.put("origin_mark_no", orderId);
        //是否使用保价费（0：不使用保价，1：使用保价； 同时，请确保填写了订单金额（cargo_price））
        data.put("is_use_insurance", 0);
        //收货码（0：不需要；1：需要。收货码的作用是：骑手必须输入收货码才能完成订单妥投）
        data.put("is_finish_code_needed", 1);
        //预约发单时间（unix时间戳(10位)，精确到分）
        //data.put("delay_publish_time", sourceId);
        //是否根据期望送达时间预约发单（0-否，即时发单；1-是，预约发单），如传1则期望送达时间必传
        //data.put("is_expect_finish_order", sourceId);
        //期望送达时间（单位秒，不早于当前时间）
        //data.put("expect_finish_time_limit", sourceId);
        //是否选择直拿直送（0：不需要；1：需要。选择直拿直送后，同一时间骑士只能配送此订单至完成，同时，也会相应的增加配送费用）
        data.put("is_direct_delivery", 0);
        //货架信息,该字段可在骑士APP订单备注中展示
        data.put("pick_up_pos", order.getStoreName());
        //取货码，在骑士取货时展示给骑士，门店通过取货码在骑士取货阶段核实骑士
        data.put("fetch_code", orderId);
        //物流配送方向(0-正向送货单、2-售后退货取件单)。如为售后单需传入发货人相关信息(售后场景下发货人一般为顾客，收货人为门店)，并请联系业务经理开通服务
        data.put("business_type", 0);
        //发货人姓名，订单发货信息默认为门店信息，如需自定义则需传发货人5项信息(发货人姓名、地址、电话、纬度、经度)，并请联系业务经理开通服务
        data.put("supplier_name", order.getStoreName());
        //发货人地址，如不传订单发货信息默认取门店信息
        data.put("supplier_address", order.getStoreDetail());
        //发货人电话，如不传订单发货信息默认取门店信息
        data.put("supplier_phone", order.getStorePhone());
        //发货人纬度，如不传订单发货信息默认取门店信息
        data.put("supplier_lat", order.getStoreLatitude());
        //发货人经度，如不传订单发货信息默认取门店信息
        data.put("supplier_lng", order.getStoreLongitude());



        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/addOrder", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 查询运费
     * @return
     */
    @GetMapping("/queryDeliverFee")
    public AjaxResult queryDeliverFee(String orderId){
        //订单商品数量
        Integer num = 0;
        FxOrderVo order = fxOrderService.selectFxOrderByOrderId(orderId);

        Map<String, Object> data = new HashMap<>();
        List<Map<String,String>> productList = new ArrayList<>();

        //组装商品信息
        for (FxOrderItem item : order.getFxOrderItemList()) {
            num += Integer.parseInt(item.getGoodCount());

            Map<String,String> product = new HashMap<>();
            //商品名称，限制长度128
            product.put("sku_name",item.getGoodName());
            //商品编码，限制长度64
            product.put("src_product_no", String.valueOf(item.getStoreGoodId()));
            //商品数量，精确到小数点后两位
            product.put("count",item.getGoodCount());
            //商品单位，默认：件
            product.put("unit","件");
            productList.add(product);
        }

        //订单商品数量
        data.put("cargo_num", num);
        //订单商品明细
        data.put("product_list", productList);
        //门店编号，门店创建后可在门店列表查看
        data.put("shop_no", sourceNo);
        //第三方订单ID
        data.put("origin_id", order.getId());
        //订单金额（单位：元）
        data.put("cargo_price", order.getPayMoney());
        //是否需要垫付 1:是 0:否 (垫付订单金额，非运费)
        data.put("is_prepay", 0);
        //收货人姓名
        data.put("receiver_name", order.getReceiveName());
        //收货人地址
        data.put("receiver_address", order.getReceiveAddress());
        //收货人地址纬度（支持火星坐标系(高德、腾讯地图))
        data.put("receiver_lat", order.getReceiveLatitude());
        //收货人地址经度（支持火星坐标系(高德、腾讯地图))
        data.put("receiver_lng", order.getReceiveLongitude());
        //回调URL（查看回调说明）
        data.put("callback", callback+"/fx/dada/addOrderCallback");
        //订单重量（单位：Kg）
        data.put("cargo_weight", 1);
        //收货人手机号（手机号和座机号必填一项），如传入隐私号，请使用英文逗号","分隔格式，并确保支持接收短信
        data.put("receiver_phone", order.getReceivePhonenum());
        //收货人座机号（手机号和座机号必填一项）
        data.put("receiver_tel", order.getReceivePhonenum());
        //订单备注
        data.put("info", order.getReceiveRemark());
        //支持配送的物品品类见链接。请选择门店真实配送品类，如无法判断可咨询您的销售经理。
        data.put("cargo_type", 25);

        //订单来源标识
        data.put("origin_mark", "");
        //订单来源编号
        data.put("origin_mark_no", orderId);
        //是否使用保价费（0：不使用保价，1：使用保价； 同时，请确保填写了订单金额（cargo_price））
        data.put("is_use_insurance", 0);
        //收货码（0：不需要；1：需要。收货码的作用是：骑手必须输入收货码才能完成订单妥投）
        data.put("is_finish_code_needed", 1);
        //预约发单时间（unix时间戳(10位)，精确到分）
        //data.put("delay_publish_time", sourceId);
        //是否根据期望送达时间预约发单（0-否，即时发单；1-是，预约发单），如传1则期望送达时间必传
        //data.put("is_expect_finish_order", sourceId);
        //期望送达时间（单位秒，不早于当前时间）
        //data.put("expect_finish_time_limit", sourceId);
        //是否选择直拿直送（0：不需要；1：需要。选择直拿直送后，同一时间骑士只能配送此订单至完成，同时，也会相应的增加配送费用）
        data.put("is_direct_delivery", 0);
        //货架信息,该字段可在骑士APP订单备注中展示
        data.put("pick_up_pos", order.getStoreName());
        //取货码，在骑士取货时展示给骑士，门店通过取货码在骑士取货阶段核实骑士
        data.put("fetch_code", orderId);
        //物流配送方向(0-正向送货单、2-售后退货取件单)。如为售后单需传入发货人相关信息(售后场景下发货人一般为顾客，收货人为门店)，并请联系业务经理开通服务
        data.put("business_type", 0);
        //发货人姓名，订单发货信息默认为门店信息，如需自定义则需传发货人5项信息(发货人姓名、地址、电话、纬度、经度)，并请联系业务经理开通服务
        data.put("supplier_name", order.getStoreName());
        //发货人地址，如不传订单发货信息默认取门店信息
        data.put("supplier_address", order.getStoreDetail());
        //发货人电话，如不传订单发货信息默认取门店信息
        data.put("supplier_phone", order.getStorePhone());
        //发货人纬度，如不传订单发货信息默认取门店信息
        data.put("supplier_lat", order.getStoreLatitude());
        //发货人经度，如不传订单发货信息默认取门店信息
        data.put("supplier_lng", order.getStoreLongitude());


        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/queryDeliverFee", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 查询运费后下单
     * @param deliveryNo
     * @return
     */
    @GetMapping("/addAfterQuery")
    public AjaxResult addAfterQuery(String deliveryNo){
        Map<String, Object> data = new HashMap<>();
        //门店编号，门店创建后可在门店列表查看
        data.put("deliveryNo", deliveryNo);
        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/addAfterQuery", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 查询订单详情
     * @param orderId
     * @return
     */
    @GetMapping("/status/query")
    public AjaxResult statusQuery(String orderId){
        Map<String, Object> data = new HashMap<>();
        //门店编号，门店创建后可在门店列表查看
        data.put("order_id", orderId);
        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/status/query", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 取消订单
     * @param orderId
     * @return
     */
    @GetMapping("/formalCancel")
    public AjaxResult formalCancel(String orderId, String cancelReasonId, String cancelReason){
        Map<String, Object> data = new HashMap<>();
        data.put("order_id", orderId);
        data.put("cancel_reason_id", cancelReasonId);
        data.put("cancel_reason", cancelReason);
        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/formalCancel", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 商户确认物品已返还
     * @param orderId
     * @return
     */
    @GetMapping("/confirm/goods")
    public AjaxResult confirmGoods(String orderId){
        Map<String, Object> data = new HashMap<>();
        data.put("order_id", orderId);
        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/confirm/goods", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    /**
     * 获取骑士配送信息H5页面
     * @param orderId
     * @return
     */
    @GetMapping("/transporter/track")
    public AjaxResult transporterTrack(String orderId){
        Map<String, Object> data = new HashMap<>();
        data.put("order_id", orderId);
        Map<String, Object> map = generateParams(data);
        String param = JSON.toJSONString(map);
        log.info(param);
        String result = HttpUtils.sendPost(devUrl + "/api/order/transporter/track", param);
        return AjaxResult.success(JSONObject.parse(result));
    }

    public static void convert(Object obj) {
        if (obj instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) obj;
            for (Object json : jsonArray) {
                convert(json);
            }
        } else if (obj instanceof JSONObject) {
            JSONObject json = (JSONObject) obj;
            Set<String> keySet = json.keySet();
            String[] keyArray = keySet.toArray(new String[keySet.size()]);
            for (String key : keyArray) {
                Object value = json.get(key);
                String[] keyStrs = key.split("_");
                if (keyStrs.length > 1) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < keyStrs.length; i++) {
                        String keyStr = keyStrs[i];
                        if (!keyStr.isEmpty()) {
                            if (i == 0) {
                                sb.append(keyStr);
                            } else {
                                int c = keyStr.charAt(0);
                                if (c >= 97 && c <= 122) {
                                    int v = c - 32;
                                    sb.append((char) v);
                                    if (keyStr.length() > 1) {
                                        sb.append(keyStr.substring(1));
                                    }
                                } else {
                                    sb.append(keyStr);
                                }
                            }
                        }
                    }
                    json.remove(key);
                    json.put(sb.toString(), value);
                }
                convert(value);
            }
        }
    }

    /**
     * 将json字符串中的key名称中包含下划线的字段，转成驼峰命名格式
     *
     * @param str String格式的JSON串
     * @return 转换后的对象（可能是JSONObject或JSONArray）
     */
    public static Object convertUnderlineToCamelCase(String str) {
        Object obj = JSON.parse(str);
        convert(obj);
        return obj;
    }
}
